package hh.util.bean;

import lombok.Data;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author huanghuai
 * @date 2020/9/10
 */
@Data
public class ClassInfo<T> implements Serializable {
    private static final long serialVersionUID = 5651712706131495482L;
    private ClassInfo<? super T> parent;
    private Class<T> clazz;
    private ReflectFieldInfo[] fieldInfos;
    private Field[] javaBeanFields;
    private Map<Field, ReflectFieldInfo> fieldToFieldInfo;
    private Map<String, ReflectFieldInfo> nameToFieldInfo;

    public ReflectFieldInfo getFieldInfo(Field field) {
        return fieldToFieldInfo.get(field);
    }

    public ReflectFieldInfo getFieldInfoWithParent(Field field) {
        ReflectFieldInfo info = fieldToFieldInfo.get(field);
        if (info != null) return info;
        return parent == null ? null : parent.getFieldInfoWithParent(field);
    }

    public ReflectFieldInfo getFieldInfo(String fieldName) {
        return nameToFieldInfo.get(fieldName);
    }

    public ReflectFieldInfo getFieldInfoWithParent(String fieldName) {
        ReflectFieldInfo info = nameToFieldInfo.get(fieldName);
        if (info != null) return info;
        return parent == null ? null : parent.getFieldInfoWithParent(fieldName);
    }

    public ClassInfo(Class<T> type) {
        this.clazz = type;
        this.javaBeanFields = ClassUtil.getMyJavaBeanFields(type);
        fieldInfos = createFieldInfos();

        fieldToFieldInfo = Arrays.stream(fieldInfos).collect(Collectors.toMap(e -> e.getField(), e -> e));
        nameToFieldInfo = Arrays.stream(fieldInfos).collect(Collectors.toMap(e -> e.getFieldName(), e -> e));

        processParent(type);
    }

    private void processParent(Class<T> type) {
        if (type.getSuperclass() != null) {
            this.parent = new ClassInfo<>(type.getSuperclass());
        }
    }

    private ReflectFieldInfo[] createFieldInfos() {
        List<ReflectFieldInfo> infos = new ArrayList<>();
        for (Field field : javaBeanFields) {
            infos.add(new ReflectFieldInfo(field, ClassUtil.findGetMethod(field), ClassUtil.findSetMethod(field)));
        }
        return infos.toArray(new ReflectFieldInfo[0]);
    }

}
